#pragma

#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
const static int defaultcap = 5;


template<class T>
class RingQueue
{
private:
    //--操作
    void P(sem_t & sem)
    {
        sem_wait(&sem);
    }
    //++操作
    void V(sem_t & sem)
    {
        sem_post(&sem);
    }

    void Lock(pthread_mutex_t & mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t & mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int cap = defaultcap)
        :ringqueue_(cap)
        ,cap_(cap)
        ,c_step_(0)
        ,p_step_(0)
    {
        sem_init(&cdata_sem_, 0, 0);
        sem_init(&pspace_sem_, 0, cap);

        pthread_mutex_init(&c_mutex_, nullptr);
        pthread_mutex_init(&p_mutex_, nullptr);
    } 

    void Push(const T& in) //生产
    {
        //先P在加锁。
        //因为信号量资源是不需要被保护的，它是原子的。而且我们要尽量的保证临界区代码较少
        //从逻辑上来说，先申请锁在申请信号量的话，那么一次最多只有一个线程可以拿到信号量，是串行的
        //先申请信号量的，在申请锁的话。那么可以先把信号量全部申请完，然后再一个个的去竞争锁。这样是并行的操作
        //所以先申请信号量的效率要更高

        P(pspace_sem_); //要申请一个空格子
        Lock(p_mutex_); //多生产者进去的时候，只让一个生产者去执行，如果是单生产者就没有必要了。
        ringqueue_[p_step_] = in; //放入数据
        //位置后移，且要维持环形特性
        p_step_++;
        p_step_ %= cap_;
        Unlock(p_mutex_);
        V(cdata_sem_); //生产了一个了，那么相应的数据也就++
        

    }
    void Pop(T* out) //消费
    {
        P(cdata_sem_); //关注的是消费者当前拥有的数据资源
        Lock(c_mutex_);
        *out = ringqueue_[c_step_]; //拿到消费的数据,消费的是c的下标
        //位置后移，因为要维持环形特性
        c_step_++;
        c_step_ %= cap_;
        Unlock(c_mutex_);
        V(pspace_sem_); //消费了一个数据，那么对应的生产者数据也要++



    }
    ~RingQueue()
    {
        sem_destroy(&cdata_sem_);
        sem_destroy(&pspace_sem_);
        pthread_mutex_destroy(&c_mutex_);
        pthread_mutex_destroy(&p_mutex_);
    }
private:
    vector<T> ringqueue_;
    int cap_;

    int c_step_; //消费者下标
    int p_step_; //生产者下标

    sem_t cdata_sem_;  //消费者关注的数据资源
    sem_t pspace_sem_; //生产者关注的空间资源

    pthread_mutex_t c_mutex_; 
    pthread_mutex_t p_mutex_;
};